package com.katze.tools.zookeeper.controller;

import com.katze.common.Message;
import com.katze.common.tree.Node;
import com.katze.tools.zookeeper.entity.ZookeeperEntity;
import com.katze.tools.zookeeper.entity.ZookeeperNode;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.imps.CuratorFrameworkState;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.ThreadUtils;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import static org.apache.zookeeper.KeeperException.Code.*;

@RestController
@RequestMapping("/zookeeper")
@ConditionalOnProperty(value = "tools.zookeeper.enable", havingValue = "true")
public class ZookeeperController implements DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(ZookeeperController.class);

    public static Map<String, CuratorFramework> connections = new ConcurrentHashMap<>();

    public ZookeeperController() {
        log.info("Zookeeper请求已装载");
    }

    @RequestMapping(value = "/exists", method = RequestMethod.GET)
    public Message exists(String id) {
        return Message.ok().data(connections.containsKey(id));
    }

    @RequestMapping(value = "/connection", method = RequestMethod.POST)
    public Message connection (@RequestBody ZookeeperEntity entity) {
        String[] items = entity.getAuthorization().split(":");
        if (items.length != 3) {
            return Message.error("认证内容格式错误,不符合'digest/auth:用户名:密码'");
        }
        CuratorFramework framework = connections.computeIfAbsent(entity.getId(), (k) -> {
            CuratorFramework curatorFramework = CuratorFrameworkFactory.builder()
                    .connectString(entity.getServer())
                    .sessionTimeoutMs(entity.getSessionTimeout())
                    .connectionTimeoutMs(entity.getConnectionTimeout())
                    .authorization(items[0], String.join(":", items[1], items[2]).getBytes(StandardCharsets.UTF_8))
                    .retryPolicy(new ExponentialBackoffRetry(5000, 60))
                    .threadFactory(ThreadUtils.newThreadFactory("Thread-Factory-" + connections.size()))
                    .build();
            curatorFramework.start();
            return curatorFramework;
        });

        try {
            if (framework.getZookeeperClient().blockUntilConnectedOrTimedOut()){
                Stat stat = framework.checkExists().forPath(entity.getForPath());
                ZookeeperNode node= new ZookeeperNode(new File(entity.getForPath()), stat);
                return Message.ok().data(node);
            } else {
                return Message.error("连接超时,请稍候再试");
            }
        } catch (Exception e){
            return Message.error("连接异常:" + e.getMessage());
        }
    }

    @RequestMapping(value = "/createNode", method = RequestMethod.POST)
    public Message createNode (@RequestBody ZookeeperEntity entity){
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                String path = framework.create().creatingParentsIfNeeded()
                        .forPath(entity.getForPath(), (entity.getData() == null? "":entity.getData()).getBytes(StandardCharsets.UTF_8));
                Stat stat = framework.checkExists().forPath(path);
                ZookeeperNode node= new ZookeeperNode(new File(entity.getForPath()), stat);
                return Message.ok(node.getName() + "节点创建成功").data(node);
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点创建失败", entity.getForPath(), e);
                return Message.error("创建失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/children", method = RequestMethod.POST)
    public Message children(@RequestBody ZookeeperEntity entity){
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                List<String> children = framework.getChildren().forPath(entity.getForPath());
                List<Node> nodes = new LinkedList<>();
                for (String child : children) {
                    String forPath = entity.getForPath().equals("/")? entity.getForPath() + child: entity.getForPath() + "/" + child;
                    Stat stat = framework.checkExists().forPath(forPath);
                    ZookeeperNode node= new ZookeeperNode(new File(forPath), stat);
                    nodes.add(node);
                }
                return Message.ok().data(nodes);
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点加载失败", entity.getForPath(), e);
                return Message.error("加载失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/detail", method = RequestMethod.POST)
    public Message detail(@RequestBody ZookeeperEntity entity){
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                Stat stat = new Stat();
                List<ACL> list = framework.getACL().storingStatIn(stat).forPath(entity.getForPath());
                StringBuilder builder = new StringBuilder();
                String[] vs = entity.getAuthorization().split(":");
                for (ACL acl : list) {
                    if (acl.getId().getScheme().equals(vs[0]) && acl.getId().getId().split(":")[0].equals(vs[1])){
                        if ((acl.getPerms() & 0x1) == 1) {
                            builder.append("Read");
                        }

                        if ((acl.getPerms() & 0x2) == 2) {
                            builder.append(builder.length() > 0? ", ":"").append("Write");
                        }

                        if ((acl.getPerms() & 0x4) == 4) {
                            builder.append(builder.length() > 0? ", ":"").append("Create");
                        }

                        if ((acl.getPerms() & 0x8) == 8) {
                            builder.append(builder.length() > 0? ", ":"").append("Delete");
                        }

                        if ((acl.getPerms() & 0x10) == 16) {
                            builder.append(builder.length() > 0? ", ":"").append("Admin");
                        }
                        break;
                    }
                }
                ZookeeperNode node= new ZookeeperNode(new File(entity.getForPath()), stat, builder.toString());
                node.setData(framework.getData().forPath(entity.getForPath()));
                return Message.ok().data(node);
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点加载失败", entity.getForPath(), e);
                return Message.error("加载失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/refresh", method = RequestMethod.POST)
    public Message refresh(@RequestBody ZookeeperEntity entity){
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                Stat stat = framework.checkExists().forPath(entity.getForPath());
                ZookeeperNode node= new ZookeeperNode(new File(entity.getForPath()), stat);

                List<String> children = framework.getChildren().forPath(entity.getForPath());
                for (String child : children) {
                    String forPath = entity.getForPath().equals("/")? entity.getForPath() + child: entity.getForPath() + "/" + child;
                    stat = framework.checkExists().forPath(forPath);
                    ZookeeperNode item = new ZookeeperNode(new File(forPath), stat);
                    node.addChildren(item);
                }
                return Message.ok(node.getName() + "节点数据已刷新").data(node);
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点加载失败", entity.getForPath(), e);
                return Message.error("加载失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/saveData", method = RequestMethod.PUT)
    public Message saveData(@RequestBody ZookeeperEntity entity) {
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                Stat stat = framework.setData().withVersion(entity.getVersion()).forPath(entity.getForPath(), entity.getData().getBytes(StandardCharsets.UTF_8));
                ZookeeperNode node= new ZookeeperNode(new File(entity.getForPath()), stat);
                return Message.ok("保存成功").data(node);
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点保存失败", entity.getForPath(), e);
                return Message.error("保存失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/deleteNode", method = RequestMethod.POST)
    public Message deleteNode(@RequestBody ZookeeperEntity entity) {
        return this.getCuratorFramework(entity.getId(), (framework) -> {
            try {
                framework.delete().deletingChildrenIfNeeded().forPath(entity.getForPath());
                return Message.ok("删除成功");
            } catch (KeeperException e) {
                return dealZookeeperException(e);
            } catch (Exception e){
                log.error("{}节点删除失败", entity.getForPath(), e);
                return Message.error("删除失败:" + e.getMessage());
            }
        });
    }

    @RequestMapping(value = "/close", method = RequestMethod.DELETE)
    public Message close(String id){
        CuratorFramework framework = connections.remove(id);
        if (framework != null && framework.getState() == CuratorFrameworkState.STARTED){
            framework.close();
        }
        return Message.ok("连接已关闭");
    }

    private Message getCuratorFramework(String id, Function<CuratorFramework, Message> func){
        CuratorFramework framework = connections.get(id);
        if (framework == null) {
            return Message.error("连接已断开，请重新连接");
        }
        return func.apply(framework);
    }

    private Message dealZookeeperException(KeeperException e){
        log.error("Zookeeper组件访问异常", e);
        switch (e.code()){
            case NOAUTH: return Message.error("无授权访问[" + e.getPath() + "]").add("level", NOAUTH);
            case NONODE: return Message.error("不存在节点[" + e.getPath() + "]").add("level", NONODE);
            case UNIMPLEMENTED: return Message.error("Zookeeper组件版本与依赖包版本不匹配").add("level", UNIMPLEMENTED);
            case BADVERSION: return Message.error("当前节点[" + e.getPath() + "]已被修改,请先刷新后再试").add("level", BADVERSION);
            default: return Message.error(e.getMessage());
        }
    }

    @Override
    public void destroy() throws Exception {
        connections.forEach((k, v)->{
            v.close();
            log.info("Zookeeper-{}连接已关闭", k.split("@")[0]);
        });
    }
}
